﻿namespace Frameworks
{
    using System.Collections;
    using UnityEngine;
    using UnityEngine.Events;

    public class Audio
    {
        public enum AudioType
        {
            Music,
            Sound,
            UISound
        }

        private enum AudioState
        {
            Stopping,
            Playing,
            Pause,
            DelayWait
        }

        private static int audioCounter = 0;        // 

        private SoundManager _soundManager;         // 
        private AudioClip _clip;                    // 音频剪辑
        private AudioState _state;                  // 播放状态
        private bool _loopInfinity;                 // 无限循环
        private int _loopCount;                     // 循环次数
        private float _delay;                       // 延时播放时间
        private float _delayeWaitTime;              // 延时剩余时间
        private bool _mute;                         // 静音
        private int _priority;                      // 优先级
        private float _pitch;                       // 速度
        private float _stereoPan;                   // 声道
        private float _spatialBlend;                // 0.0->2D, 1.0->3D
        private float _reverbZoneMix;               // 混响
        private float _dopplerLevel;                // 多普勒音阶
        private float _spread;                      // 扬声器空间中3d立体声或多声道声音的传播角度(以度为单位)
        private AudioRolloffMode _rolloffMode;      // 距离衰减
        private float _max3DDistance;               // 声音停止衰减的距离
        private float _min3DDistance;               // 在最小距离内,音频将停止增大音量
        private float _targetVolume;                // 淡入/淡出的目标音量
        private float _onFadeStartVolume;           // 开始淡入淡出之前的音量,未设置为当前音量
        private Transform _sourceTransform;         // 
        private IEnumerator _coroutineFadeIn;       // 
        private IEnumerator _coroutineFadeOut;      // 
        private IEnumerator _coroutinePlay;         // 

        public int AudioID { get; private set; }                // ID
        public AudioType Type { get; private set; }             // 类型
        public float Volume { get; private set; }               // 当前音量
        public AudioSource AudioSource { get; private set; }    // AudioSource
        public bool Pooled { get; set; }                        // 
        public bool Persist { get; set; }                       // 音频是否在场景变化之间持续存在
        public float FadeInSeconds { get; set; }                // 音频淡入/达到目标音量所需的秒数(高于当前音量)
        public float FadeOutSeconds { get; set; }               // 音频淡出/达到目标音量所需的秒数(低于当前音量)
        public UnityAction OnStartBefore { get; set; }          // 
        public UnityAction OnStart { get; set; }                // 
        public UnityAction OnComplete { get; set; }             // 
        public UnityAction OnCompleteAfter { get; set; }        // 

        /// <summary>
        /// 音频
        /// </summary>
        public AudioClip Clip
        {
            get { return _clip; }
            set
            {
                _clip = value;
                if (AudioSource != null)
                {
                    AudioSource.clip = _clip;
                }
            }
        }

        /// <summary>
        /// 是否静音
        /// </summary>
        public bool Mute
        {
            get { return _mute; }
            set
            {
                _mute = value;
                if (AudioSource != null)
                {
                    AudioSource.mute = _mute;
                }
            }
        }

        /// <summary>
        /// 优先级
        /// </summary>
        public int Priority
        {
            get { return _priority; }
            set
            {
                _priority = Mathf.Clamp(value, 0, 256);
                if (AudioSource != null)
                {
                    AudioSource.priority = _priority;
                }
            }
        }

        /// <summary>
        /// 音高
        /// </summary>
        public float Pitch
        {
            get { return _pitch; }
            set
            {
                _pitch = Mathf.Clamp(value, -3, 3);
                if (AudioSource != null)
                {
                    AudioSource.pitch = _pitch;
                }
            }
        }

        /// <summary>
        /// 声道
        /// </summary>
        public float StereoPan
        {
            get { return _stereoPan; }
            set
            {
                _stereoPan = Mathf.Clamp(value, -1, 1);
                if (AudioSource != null)
                {
                    AudioSource.panStereo = _stereoPan;
                }
            }
        }

        /// <summary>
        /// 0.0->2D, 1.0->3D
        /// </summary>
        public float SpatialBlend
        {
            get { return _spatialBlend; }
            set
            {
                _spatialBlend = Mathf.Clamp01(value);
                if (AudioSource != null)
                {
                    AudioSource.spatialBlend = _spatialBlend;
                }
            }
        }

        /// <summary>
        /// 混响
        /// </summary>
        public float ReverbZoneMix
        {
            get { return _reverbZoneMix; }
            set
            {
                _reverbZoneMix = Mathf.Clamp(value, 0, 1.1f);
                if (AudioSource != null)
                {
                    AudioSource.reverbZoneMix = _reverbZoneMix;
                }
            }
        }

        /// <summary>
        /// 多普勒音阶
        /// </summary>
        public float DopplerLevel
        {
            get { return _dopplerLevel; }
            set
            {
                _dopplerLevel = Mathf.Clamp(value, 0, 5);
                if (AudioSource != null)
                {
                    AudioSource.dopplerLevel = _dopplerLevel;
                }
            }
        }

        /// <summary>
        /// 扬声器空间中3d立体声或多声道声音的传播角度(以度为单位)
        /// </summary>
        public float Spread
        {
            get { return _spread; }
            set
            {
                _spread = Mathf.Clamp(value, 0, 360);
                if (AudioSource != null)
                {
                    AudioSource.spread = _spread;
                }
            }
        }

        /// <summary>
        /// 距离衰减
        /// </summary>
        public AudioRolloffMode RolloffMode
        {
            get { return _rolloffMode; }
            set
            {
                _rolloffMode = value;
                if (AudioSource != null)
                {
                    AudioSource.rolloffMode = _rolloffMode;
                }
            }
        }

        /// <summary>
        /// 声音停止衰减的距离
        /// </summary>
        public float Max3DDistance
        {
            get { return _max3DDistance; }
            set
            {
                _max3DDistance = Mathf.Max(value, 0.01f);
                if (AudioSource != null)
                {
                    AudioSource.maxDistance = _max3DDistance;
                }
            }
        }

        /// <summary>
        /// 在最小距离内,音频将停止增大音量
        /// </summary>
        public float Min3DDistance
        {
            get { return _min3DDistance; }
            set
            {
                _min3DDistance = Mathf.Max(value, 0);
                if (AudioSource != null)
                {
                    AudioSource.minDistance = _min3DDistance;
                }
            }
        }

        public float Length
        {
            get
            {
                if (_state == AudioState.Stopping || _state == AudioState.DelayWait)
                    return 0.0f;

                return Mathf.Clamp01((AudioSource.time / AudioSource.pitch) / (AudioSource.clip.length / AudioSource.pitch));
            }
        }

        public bool IsActive { get { return (_state != AudioState.Stopping); } }
        public bool IsPlaying { get { return (_state == AudioState.Playing || _state == AudioState.DelayWait); } }
        public bool IsPaused { get { return (_state == AudioState.Pause); } }

        public Audio(AudioType audioType, AudioClip clip, bool loopInfinity, int loopCount, float delay, bool persist,
            float volume, float fadeInValue, float fadeOutValue, Transform sourceTransform, SoundManager manager,
            UnityAction onStartBefore, UnityAction onStart, UnityAction onComplete, UnityAction onCompleteAfter)
        {
            // ID
            AudioID = audioCounter;
            audioCounter++;

            // 初始值
            Type = audioType;
            Clip = clip;
            _loopInfinity = loopInfinity;
            _loopCount = loopCount;
            _delay = delay;
            Persist = persist;
            _targetVolume = volume;
            FadeInSeconds = fadeInValue;
            FadeOutSeconds = fadeOutValue;

            _soundManager = manager;
            if (sourceTransform == null) _sourceTransform = _soundManager.Mono.transform;
            else _sourceTransform = sourceTransform;

            Volume = 0f;
            Pooled = false;

            // 默认值
            Mute = false;
            Priority = 128;
            Pitch = 1;
            StereoPan = 0;
            if (sourceTransform != null && sourceTransform != _soundManager.Mono.transform)
            {
                SpatialBlend = 1;
            }
            ReverbZoneMix = 1;
            DopplerLevel = 1;
            Spread = 0;
            RolloffMode = AudioRolloffMode.Logarithmic;
            Min3DDistance = 1;
            Max3DDistance = 500;

            // 状态
            _state = AudioState.Stopping;

            // Event
            if (onStartBefore != null) OnStartBefore += onStartBefore;
            if (onStart != null) OnStart += onStart;
            if (onComplete != null) OnComplete += onComplete;
            if (onCompleteAfter != null) OnCompleteAfter += onCompleteAfter;
        }

        /// <summary>
        /// 创建并初始化 AudioSource 组件
        /// </summary>
        private void CreateAudiosource()
        {
            AudioSource = _sourceTransform.gameObject.AddComponent<AudioSource>() as AudioSource;
            AudioSource.clip = Clip;
            AudioSource.loop = (_loopInfinity || _loopCount > 1) ? true : false;
            AudioSource.mute = Mute;
            AudioSource.volume = Volume;
            AudioSource.priority = Priority;
            AudioSource.pitch = Pitch;
            AudioSource.panStereo = StereoPan;
            AudioSource.spatialBlend = SpatialBlend;
            AudioSource.reverbZoneMix = ReverbZoneMix;
            AudioSource.dopplerLevel = DopplerLevel;
            AudioSource.spread = Spread;
            AudioSource.rolloffMode = RolloffMode;
            AudioSource.maxDistance = Max3DDistance;
            AudioSource.minDistance = Min3DDistance;
            AudioSource.playOnAwake = false;
            //AudioSource.outputAudioMixerGroup = _soundManager.AudioMixerGroup;
        }

        public void Play()
        {
            Play(_targetVolume);
        }

        public void Play(float volume)
        {
            // 检查声音管理器中是否仍存在音频
            if (Pooled)
            {
                bool restoredFromPool = _soundManager.RestoreAudioFromPool(Type, AudioID);
                if (!restoredFromPool) return;

                Pooled = true;
            }

            if (AudioSource == null) CreateAudiosource();

            // StartBefore Event
            if (OnStartBefore != null) OnStartBefore.Invoke();

            _targetVolume = volume;
            _coroutineFadeIn = _PlayFadeIn();
            _soundManager.Mono.StartCoroutine(_coroutineFadeIn);
        }

        private IEnumerator _PlayFadeIn()
        {
            _state = AudioState.DelayWait;
            // 延时等待
            _delayeWaitTime = 0.0f;
            while (_delayeWaitTime < _delay)
            {
                _delayeWaitTime += Time.deltaTime;
                yield return new WaitForEndOfFrame();
            }

            // Start Event
            if (OnStart != null) OnStart.Invoke();

            _state = AudioState.Playing;
            AudioSource.Play();

            float fadeInTerpolater = 0f;
            float fadeStartVolume = Volume;
            do
            {
                fadeInTerpolater += Time.unscaledDeltaTime;

                if (FadeInSeconds <= 0) Volume = _targetVolume;
                else Volume = Mathf.Lerp(fadeStartVolume, _targetVolume, fadeInTerpolater / FadeInSeconds);

                SyncVolum();
                yield return new WaitForEndOfFrame();
            } while (Volume < _targetVolume);

            _coroutineFadeIn = null;
            _coroutinePlay = _Play();
            _soundManager.Mono.StartCoroutine(_coroutinePlay);
        }

        private IEnumerator _Play()
        {
            if (!_loopInfinity) _loopCount--;
            
            _delayeWaitTime = 0.0f;
            while (_delayeWaitTime < AudioSource.clip.length / AudioSource.pitch)
            {
                _delayeWaitTime += Time.deltaTime;
                yield return new WaitForEndOfFrame();
            }

            // Complete Event
            if (OnComplete != null) OnComplete.Invoke();

            // 检查结束
            if (_loopCount <= 0)
            {
                // CompleteAfter Event
                if (OnCompleteAfter != null) OnCompleteAfter.Invoke();

                _state = AudioState.Stopping;
                AudioSource.Stop();
                _soundManager.RemoveStopAudio(this);
                if (_coroutineFadeIn != null)
                {
                    _soundManager.Mono.StopCoroutine(_coroutineFadeIn);
                    _coroutineFadeIn = null;
                }
                if (_coroutinePlay != null)
                {
                    _soundManager.Mono.StopCoroutine(_coroutinePlay);
                    _coroutinePlay = null;
                }
                yield break;
            }
            else
            {
                _coroutineFadeIn = _Play();
                _soundManager.Mono.StartCoroutine(_coroutineFadeIn);
                yield break;
            }
        }

        public void Stop()
        {
            if (_state == AudioState.DelayWait || _state == AudioState.Playing || _state == AudioState.Pause)
            {
                _state = AudioState.Stopping;

                if (_coroutineFadeIn != null)
                {
                    _soundManager.Mono.StopCoroutine(_coroutineFadeIn);
                    _coroutineFadeIn = null;
                }
                if (_coroutinePlay != null)
                {
                    _soundManager.Mono.StopCoroutine(_coroutinePlay);
                    _coroutinePlay = null;
                }

                _targetVolume = 0;
                _coroutineFadeOut = _Stop();
                _soundManager.Mono.StartCoroutine(_coroutineFadeOut);
            }
        }

        private IEnumerator _Stop()
        {
            float fadeOutTerpolater = 0f;
            float fadeStartVolume = Volume;
            do
            {
                fadeOutTerpolater += Time.unscaledDeltaTime;

                if (FadeOutSeconds <= 0) Volume = 0;
                else Volume = Mathf.Lerp(fadeStartVolume, _targetVolume, fadeOutTerpolater / FadeOutSeconds);

                SyncVolum();
                yield return new WaitForEndOfFrame();
            } while (Volume > _targetVolume);

            AudioSource.Stop();
            _soundManager.RemoveStopAudio(this);

            OnStartBefore = null;
            OnStart = null;
            OnComplete = null;
            OnCompleteAfter = null;
        }

        public void Pause()
        {
            if (_state == AudioState.DelayWait || _state == AudioState.Playing)
            {
                if (_coroutineFadeIn != null) _soundManager.Mono.StopCoroutine(_coroutineFadeIn);
                if (_coroutinePlay != null) _soundManager.Mono.StopCoroutine(_coroutinePlay);
                AudioSource.Pause();

                _state = AudioState.Pause;
            }
        }

        public void UnPause()
        {
            if (_state == AudioState.Pause)
            {
                if (_coroutineFadeIn != null) _soundManager.Mono.StartCoroutine(_coroutineFadeIn);
                if (_coroutinePlay != null) _soundManager.Mono.StartCoroutine(_coroutinePlay);
                AudioSource.UnPause();

                _state = AudioState.Playing;
            }
        }

        public void Resume()
        {
            if (_state == AudioState.Pause)
            {
                if (_coroutineFadeIn != null) _soundManager.Mono.StartCoroutine(_coroutineFadeIn);
                if (_coroutinePlay != null) _soundManager.Mono.StartCoroutine(_coroutinePlay);
                AudioSource.UnPause();

                _state = AudioState.Playing;
            }
        }

        public void SetVolume(float volume)
        {
            if (volume > _targetVolume)
            {
                SetVolume(volume, FadeOutSeconds);
            }
            else
            {
                SetVolume(volume, FadeInSeconds);
            }
        }

        public void SetVolume(float volume, float fadeSeconds)
        {
            SetVolume(volume, fadeSeconds, Volume);
        }

        /// <summary>
        /// 设置音量
        /// </summary>
        /// <param name="volume">目标音量</param>
        /// <param name="fadeSeconds">音频淡入/淡出达到目标音量所需的秒数</param>
        /// <param name="startVolume">开始淡入淡出之前的音量,未设置为当前音量</param>
        public void SetVolume(float volume, float fadeSeconds, float startVolume)
        {
            _targetVolume = Mathf.Clamp01(volume);
            _onFadeStartVolume = startVolume;
            FadeOutSeconds = FadeInSeconds = fadeSeconds;
        }

        /// <summary>
        /// 设置音频3D距离
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        public void Set3DDistances(float min, float max)
        {
            Min3DDistance = min;
            Max3DDistance = max;
        }

        private void SyncVolum()
        {
            switch (Type)
            {
                case AudioType.Music:
                    AudioSource.volume = Volume * _soundManager.GlobalMusicVolume * _soundManager.GlobalVolume;
                    break;
                case AudioType.Sound:
                    AudioSource.volume = Volume * _soundManager.GlobalSoundsVolume * _soundManager.GlobalVolume;
                    break;
                case AudioType.UISound:
                    AudioSource.volume = Volume * _soundManager.GlobalUISoundsVolume * _soundManager.GlobalVolume;
                    break;
                default: break;
            }
        }
    }
}
